﻿#include <iostream>
#include <functional>

int foo() { return 5; }
int goo() { return 6; }
int sum(int a, int b) { return a + b; }

bool Max(int a, int b) {
	return a > b ? true : false;
}


//入参为其他函数的指针
int max(int a, int b, bool (*Max)(int, int))
{
	if (Max(a, b)) return a;
	else return b;
}

//提供指针函数的默认值: bool (*mmax)(int, int)= Max
int max2(int a, int b, bool (*mmax)(int, int) = Max)
{
	if (mmax(a, b)) return a;
	else return b;
}


//使用类型别名使函数指针更漂亮
using ValidateFunction = bool(*)(int, int);
int validate(int x, int y, ValidateFunction pfcn) {
	if (pfcn(x, y)) return x;
	else return y;
}

//定义和存储函数指针的另一种方法是使用std::function，
// 它是标准库<functional>头文件的一部分
// std::function 是一个接收两个int返回一个bool的函数
int validate2(int x, int y, std::function<bool(int, int)> fcn) {
	if (fcn(x, y)) return x;
	else return y;
}


int main()
{
	std::cout << "Hello World!\n";


	//将函数分配给函数指针
	int (*fcnPtr)() { &foo }; // fcnPtr 指向 foo
	fcnPtr = &goo; // fcnPtr 现在指向 goo

	//与基本类型不同，如果需要，C++将隐式地将函数转换为函数指针
   //（因此不需要使用 操作符 & 来获取函数的地址）。然而，函数指针不能转换为void指针

	//使用函数指针调用函数
	int ss = (*fcnPtr)(); //显式解引用: 通过 fcnPtr 调用函数 foo(5)
	ss = fcnPtr(); //隐式解引用: 通过 fcnPtr 调用函数 foo(5)
	std::cout << ss << "\n";

	int (*fcnPtr2)(int, int) { &sum }; // fcnPtr2 指向带参函数sum,
	//调用并获取返回值
	int sm = fcnPtr2(33, 56);
	std::cout << sm << "\n";


	bool (*MMax)(int, int) { &Max };
	//将函数作为参数传递给其他函数
	//用作另一个函数的参数的函数有时称为回调函数。
	int mmx = max(45, 65, MMax);
	int mmx2 = max2(6666, 32);
	int mmx3 = validate(6666, 32, MMax);
	int mmx4 = validate2(6666, 32, MMax);


	//函数指针的类型推导
	auto fcnPtr666{ &foo };
	std::cout << fcnPtr666() << '\n';



	/*
	* 堆和栈
	程序使用的内存通常分为几个不同的区域，称为段：
	代码段（也称为文本段），编译后的程序二进制位于内存中。代码段通常是只读的。
	bss段（也称为未初始化的数据段），其中存储零初始化的全局变量和静态变量。
	数据段（也称为初始化数据段），其中存储初始化的全局变量和静态变量。
	堆，存储分配动态分配的变量。
	调用栈，其中存储函数参数、局部变量和其他函数相关信息。
	
	堆段（也称为“空闲存储”）跟踪用于动态分配的内存。
	当使用new操作符分配内存时，该内存分配在应用程序的堆段中。

	堆有优点和缺点：
    在堆上分配内存相对较慢。
    分配的内存保持分配状态，直到它被主动释放（当心内存泄漏）或应用程序结束（此时操作系统应该清理它）。
    必须通过指针访问动态分配的内存。解引用指针比直接访问变量慢。
    因为堆是一个大的内存池，所以可以在这里分配大的数组、结构体或类。
	*/






}
